iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
佛心分享-IT 人技術創業

Berry AI:從零開始打造全美第一的得來速 Vision AI系列 第 10

免桌面環境的 kiosk 顯示方案:Ubuntu Frame + Chromium 打造多相機即時監控牆

  • 分享至 

  • xImage
  •  

施工廠商到店家安裝相機時,除了數量與位置要正確,相機的角度與涵蓋範圍也要符合事前的模擬,所以現場的施工人員要能一次看到所有相機的即時畫面,逐一確認。IP cam 都有內建 Web UI 可以觀看串流,但對非資訊專長的施工人員來說,逐支登入相機的 Web UI 難度太高,因此我們在 edge server 上做了一面多相機的即時監控牆 — Camera Grid。門市現場除了 edge server,還會安裝一台顯示器,它正式上線後的工作是顯示 Day 01 提過的 Drive-thru Real-Time Dashboard;相機安裝階段還沒有數據可看,這台顯示器就先借來當監控牆。先列出基本需求:

  • 畫面輸出到現場的顯示器,開機即顯示
  • 依相機數量自動調整版面,在單一畫面呈現所有相機的串流
  • 能操作變焦鏡頭拉近或拉遠
  • 同時顯示 edge server 的網路狀態

這面監控牆分成三層:最底下是 kiosk 顯示層,Ubuntu Frame 加 Chromium 讓 edge server 不裝桌面環境就能全螢幕顯示網頁;中間是串流層,MediaMTX 把 RTSP 轉成瀏覽器能播放的協定;最上層是裝置管理,Camera Manager 維護相機清單並彙整狀態資料。以下依序說明。

相機的 RTSP 串流由 MediaMTX 轉成 HLS,Camera Grid 呈現在 Chromium 全螢幕頁面並輸出到顯示器,Camera Manager 維護相機清單與各相機的狀態

實線是影像與資料的流向,虛線是 Camera Manager 發出的控制指令;顯示器接在 edge server 上。

沒有桌面環境的 Ubuntu Server,如何開機就全螢幕顯示網頁?

加裝桌面環境當然可行,但我們希望 edge server 在沒有登入、也沒有任何人工操作 (真實環境中沒有滑鼠鍵盤) 的狀況下,開完機就直接顯示指定的網頁。

Canonical 在 2021 年 10 月推出的 Ubuntu Frame,正是為這種需求設計的顯示伺服器,專門服務 kiosk、數位看板這類「一台機器只跑一個全螢幕應用」的場景。它底層建構在 Mir (Canonical 開發的 Wayland compositor 函式庫) 之上,對應用程式提供標準的 Wayland 介面;以 snap 的形式發布,不需要安裝桌面環境,也沒有工作列、視窗標題列或切換視窗的快捷鍵,開機後什麼都不會顯示,等著應用程式把畫面填滿。安裝後設定 daemon=true,Ubuntu Frame 就會註冊成系統服務,每次開機在無人登入的情況下自動啟動並接管顯示輸出。

有了顯示伺服器,接著要決定用哪個瀏覽器來顯示網頁。Canonical 官方教學搭配的是輕量的 WPE WebKit,但 Chromium 的網頁相容性與除錯工具都成熟得多,所以我們選擇 Chromium。

Ubuntu Frame 與 Chromium 的安裝設定

安裝順序是先 Ubuntu Frame 再 Chromium:前者接管顯示輸出,後者是掛在它上面的唯一一個應用程式。中間那段 screenshot 指令是驗證用的,edge server 通常不在手邊,遠端擷取一張畫面就能確認輸出正常。

sudo snap install ubuntu-frame
sudo snap set ubuntu-frame daemon=true
# 設定顯示器輸出的解析度與縮放
sudo snap set ubuntu-frame display="
layouts:
  default:
    cards:
    - card-id: 0
      HDMI-A-1:
        mode: 1920x1080@60.0
        scale: 1
        group: 0
"
# screenshot 工具需要這個 plug 才能連上 Frame
sudo snap connect ubuntu-frame:wayplug
# 擷取螢幕畫面,在支援 imgcat 的終端機直接檢視
sudo mv $(sudo ubuntu-frame.screenshot) ss.png && cat ss.png | imgcat

# 安裝 Chromium
sudo snap install chromium
sudo snap connect chromium:wayland
sudo snap connect chromium:network-bind
sudo snap set chromium daemon=true

# 設定 Chromium 要顯示的網址,9527 是 Camera Grid 網頁的 port
sudo snap set chromium url=http://localhost:9527/

Chromium snap 的 daemonurl 這兩個設定並沒有記載在官方文件中,是翻閱 chromium snap 的打包設定之後找到的用法,我們已在正式環境長期使用。

這個組合的好處是整套顯示功能只由兩個 snap 組成:沒有桌面環境要維護,也不用擔心螢幕保護、自動更新提示或系統通知跳出來蓋住畫面。施工廠商把顯示器接上 edge server 開機,等一分鐘左右整面監控牆就會填滿所有相機的即時畫面 — 剩下調整角度與變焦的工作,交給 Camera Grid 處理。

顯示什麼內容完全由 url 這一個設定決定,所以門市驗收完、要換成 Drive-thru Real-Time Dashboard 時,kiosk 這一層不用動:

# 門市上線,同一台顯示器從監控牆換成營運看板
sudo snap set chromium url=https://<dashboard-url>/
sudo snap restart chromium.daemon

這兩個 snap 我們踩過的坑不少:版本並不是越新越好,兩者的組合也可能有相容性問題,所以測試過後要把版本都鎖定,塞進 Day 05 提到的那支安裝 USB,在 autoinstall 階段離線安裝:

# 辦公室,有網路的機器:下載並打包,以 Chromium 為例
snap download chromium
assert_file=$(ls | grep "chromium_.*\.assert")
snap_file=$(ls | grep "chromium_.*\.snap")
current_date=$(date +%Y%m%d)
tar -czvf "chromium_snap_$current_date.tgz" "$assert_file" "$snap_file"

打包好的 .tgz 跟著 ISO 一起進 USB,產線上還原:

# 產線,離線的出貨機:還原並安裝
tar -xzvf chromium_snap_20231116.tgz
assert_file=$(ls | grep "chromium_.*\.assert")
snap_file=$(ls | grep "chromium_.*\.snap")
sudo snap ack "$assert_file"
sudo snap install "$snap_file"

snap 預設會不定時自動更新,要讓版本真的鎖住,還得關閉自動 refresh (snapd 2.58 起支援 --hold):

# 鎖定特定 package,例如 ubuntu-frame 與 Chromium
sudo snap refresh --hold=forever ubuntu-frame chromium

# 鎖定整個系統的所有 snap
sudo snap refresh --hold=forever

讓瀏覽器播放多路相機串流

瀏覽器不支援 RTSP,中間需要一個轉封裝的角色,這正是 Day 07 介紹過的 MediaMTX 的工作:它把每支相機的 RTSP 以 source proxy 的方式拉進來 (由 MediaMTX 主動連上相機取流,相機端不必改設定),每支相機對應 MediaMTX 上的一條 path,同一條 path 可同時以 HLS 與 WebRTC 對外提供,前端把播放來源指向 http://<edge-ip>:8888/<camId>/index.m3u8,用 ReactPlayer (React 生態系的播放器元件) 就能播放。

嵌入頁面的部分並不複雜,ReactPlayer 遇到 .m3u8 的來源會自動載入 hls.js 處理,所以沒有原生 HLS 支援的 Chromium 也能播放。骨架大致如下 (以 v3 的 API 為例,v2 的 url prop 在 v3 改名為 src):

import ReactPlayer from "react-player";

// cams 是相機清單,來自 Camera Manager 產出的 JSON (下一節)
function CameraGrid({ cams }) {
  return (
    <div className="grid">
      {cams.map((cam) => (
        <ReactPlayer
          key={cam.id}
          src={`http://${location.hostname}:8888/${cam.id}/index.m3u8`}
          playing
          muted
          width="100%"
          height="100%"
        />
      ))}
    </div>
  );
}

比較容易被忽略的是 Chromium 的自動播放政策:沒有使用者互動的頁面只允許靜音影片自動播放,所以 muted 必須設定,否則 kiosk 開機後畫面會停在第一幀。

相機清單與狀態的自動維護

相機清單不能寫死:一間門市裝幾支相機、每支的 IP 由 DHCP 配到哪,都是到了現場才知道。所以 edge server 上有一支常駐的管理程式 Camera Manager,定期做三件事:

  • 用 ONVIF (Day 08 介紹過的監控設備通用介面標準,涵蓋裝置搜尋、設定與鏡頭控制) 掃描網段內所有相機,取得 IP、型號與 RTSP URL,以 MAC Address 當識別碼;若主串流的編碼是 H.265,也透過 ONVIF 改成瀏覽器支援度較完整的 H.264,因為 MediaMTX 只轉封裝、不轉碼
  • 呼叫 MediaMTX 的 Control API,依掃描結果 add / patch / delete,維持串流清單與現場一致
  • 從 Control API 撈出每支相機的來源、型號、串流是否 ready 與收到的位元組數,加上 hostname、各網卡的 IP 與連線速度 (含 VPN 介面),寫成一份 JSON 檔供 Camera Grid 取用

Camera Grid 的網頁本身也由 edge server 提供,前端 fetch 這份 JSON 後,依相機數量自動決定版面的欄列數,把沒 ready 的相機標紅,之後每 60 秒重抓一次 JSON 更新狀態;至於變焦,按鈕呼叫的是 Camera Manager 的 API,由它透過 ONVIF 對相機下指令。

小結

回顧這面監控牆的三層:Ubuntu Frame 加 Chromium 撐起不用桌面環境的 kiosk 顯示層,MediaMTX 把 RTSP 轉成瀏覽器播得動的 HLS,Camera Manager 維護相機清單與狀態。施工廠商把顯示器接上 edge server,開機就能看到所有相機的即時畫面與網路狀態,全程沒有人登入這台機器,也沒有桌面環境要維護。這套組合的前提是版本鎖定:snap 的自動更新造成過相容性問題,所有 snap 都必須測試後鎖版、離線安裝 — 代價是安全性更新也一併被擋下,後續的更新必須改由自己的部署機制接手。

參考資料


本系列由 Berry AI 工程團隊出品。更多工程實戰紀錄都在 Berry AI 技術部落格


上一篇
如何規模化取得每支相機的內部參數?單相機校準 (Single Camera Calibration) 產線 SOP
系列文
Berry AI:從零開始打造全美第一的得來速 Vision AI10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言